vim9script

# --------------------------
# Cmdline auto-completion
# --------------------------
set wim=noselect:lastused,full wop=fuzzy,pum,tagfile wmnu

autocmd CmdlineChanged : CmdComplete()
def CmdComplete()
    var cmdline = getcmdline()
    if cmdline =~ '^\s*\%(Grep\|Find\|Buffer\|Oldfiles\|GGrep\)'
        wildtrigger()
    endif
enddef

cnoremap <expr> <Up>   wildmenumode() ? "\<C-E>\<Up>"   : "\<Up>"
cnoremap <expr> <Down> wildmenumode() ? "\<C-E>\<Down>" : "\<Down>"


# --------------------------
# Fuzzy find file
# --------------------------
command! -nargs=* -complete=customlist,FuzzyFind Find FindOpen()
var allfiles: list<string>
autocmd CmdlineEnter : allfiles = null_list
def GetGitDirectory(): string
    try
        var git_dir = system('git rev-parse --show-toplevel')
        if v:shell_error == 0
            return trim(git_dir) .. '/'
        endif
    catch
        return '.'
    endtry
    return '.'
enddef
def FuzzyFind(arglead: string, _: string, _: number): list<string>
    if allfiles == null_list
        # allfiles = systemlist($'find {get(g:, "fzfind_root", ".")} \! \( -path "*/.git" -prune -o -name "*.swp" \) -type f -follow')
        var dir_root = GetGitDirectory()
        if dir_root == "."
            allfiles = systemlist($'fd --type f --color never')
        else
            allfiles = systemlist($'git -C ' .. dir_root .. ' ls-files -c -o --exclude-standard')
        endif
    endif
    return arglead == '' ? allfiles : allfiles->matchfuzzy(arglead)
enddef
def FindOpen()
    if (selected_match != null_string)
        var dir_root = GetGitDirectory()
        if dir_root != "."
            selected_match = dir_root .. selected_match
        endif
        exec $'edit {selected_match}'
    endif
enddef
nnoremap <leader>ff :Find<space>


# --------------------------
# Live grep
# --------------------------
command! -nargs=+ -complete=customlist,GrepComplete Grep VisitFile()
def GrepComplete(arglead: string, cmdline: string, cursorpos: number): list<any>
    # return arglead->len() > 1 ? systemlist($'grep -REIHns "{arglead}"' .. ' --exclude-dir=.git --exclude=".*" --exclude="tags" --exclude="*.swp"') : []
    return arglead->len() > 2 ? systemlist($'rg -M 200 --max-columns-preview --vimgrep --smart-case --color=never "{arglead}"')[: 500] : []
enddef
def VisitFile()
    if (selected_match != null_string)
        var qfitem = getqflist({lines: [selected_match]}).items[0]
        if qfitem->has_key('bufnr') && qfitem.lnum > 0
            var pos = qfitem.vcol > 0 ? 'setcharpos' : 'setpos'
            exec $':b +call\ {pos}(".",\ [0,\ {qfitem.lnum},\ {qfitem.col},\ 0]) {qfitem.bufnr}'
            setbufvar(qfitem.bufnr, '&buflisted', 1)
        endif
    endif
enddef
nnoremap <leader>ss :Grep<space>
nnoremap <leader>sd :Grep <c-r>=expand("<cword>")<cr>

command! -nargs=+ -complete=customlist,GGrepComplete GGrep GVisitFile()
def GGrepComplete(arglead: string, cmdline: string, cursorpos: number): list<any>
    var complList = []
    if arglead->len() > 2
      var curDir = getcwd()
      execute 'silent! cd ' .. GetGitDirectory()
      complList = systemlist($'rg -M 200 --max-columns-preview --vimgrep --smart-case --color=never "{arglead}"')[: 500]
      execute 'silent! cd ' .. curDir
    endif
    return complList
enddef
def GVisitFile()
    if (selected_match != null_string)
        var curDir = getcwd()
        execute 'silent! cd ' .. GetGitDirectory()
        var qfitem = getqflist({lines: [selected_match]}).items[0]
        if qfitem->has_key('bufnr') && qfitem.lnum > 0
            var pos = qfitem.vcol > 0 ? 'setcharpos' : 'setpos'
            exec $':b +call\ {pos}(".",\ [0,\ {qfitem.lnum},\ {qfitem.col},\ 0]) {qfitem.bufnr}'
            setbufvar(qfitem.bufnr, '&buflisted', 1)
        endif
        if !&autochdir
            execute 'silent! cd ' .. curDir
        endif
    endif
enddef
nnoremap <leader>sg :GGrep<space>


# --------------------------
# Fuzzy find buffer
# --------------------------
command! -nargs=* -complete=customlist,FuzzyBuffer Buffer execute('b ' .. selected_match->matchstr('\d\+'))
def FuzzyBuffer(arglead: string, _: string, _: number): list<string>
    var bufs = execute('buffers', 'silent!')->split("\n")
    var altbuf = bufs->indexof((_, v) => v =~ '^\s*\d\+\s\+#')
    if altbuf != -1
        [bufs[0], bufs[altbuf]] = [bufs[altbuf], bufs[0]]
    endif
    return arglead == '' ? bufs : bufs->matchfuzzy(arglead)
enddef
nnoremap <leader>bb :Buffer<space>


# --------------------------
# Fuzzy find oldfiles
# --------------------------
command! -nargs=* -complete=customlist,FuzzyOldfiles Oldfiles execute('edit ' .. selected_match)
def FuzzyOldfiles(arglead: string, _: string, _: number): list<string>
    var files = filter(copy(v:oldfiles), "filereadable(fnamemodify(v:val, ':p'))")
    return arglead == '' ? files : files->matchfuzzy(arglead)
enddef
nnoremap <leader>bh :Oldfiles<space>


# --------------------------
# Utilities
# --------------------------
var selected_match = null_string
autocmd CmdlineLeavePre : SelectItem()
def SelectItem()
    selected_match = ''
    if getcmdline() =~ '^\s*\%(Grep\|Find\|Buffer\|Oldfiles\|GGrep\)\s'
        var info = cmdcomplete_info()
        if info != {} && info.pum_visible && !info.matches->empty()
            selected_match = info.selected != -1 ? info.matches[info.selected] : info.matches[0]
            setcmdline(info.cmdline_orig)
        endif
    endif
enddef
